Implementing the Background Picture Extension Functions
This section describes the functions of the background picture printing extension. This extension needs to accomplish the following tasks:
- Initialize the environment.
- Add a panel to the Print dialog box so that the user can make several choices: whether to enable or disable this extension, which picture to use as the background picture, and at what intensity level to draw the picture on the page.
- Respond to events that occur in the panel, such as a mouse click or keypress.
- Store the background picture in the spool file along with the document that is being spooled.
- Draw the selected background picture on each page of a document that is being despooled.
- Deallocate the storage allocated for the background picture shape that was spooled.
- Shut down the environment at the end of the program.
Initializing the Extension Environment
The background picture printing extension performs very simple initialization. Its override of theGXInitialize
message,BWInitialize
, allocates a globals world so that it has the global data needed for a printing extension. TheGXInitialize
message is described on page 4-43 in the chapter "Printing Messages." The code for theBWInitialize
function is shown in Listing 2-6.Listing 2-6 The
BWInitialize
override function
OSErr BWInitialize() { OSErr err = noErr; err = NewMessageGlobals(A5Size(), A5Init); if (!err) err = InitGlobalData(); return err; }TheBWInitialize
function first sets up an A5 world, which you must do in your extension if you are going to use global data. Setting up an A5 world means setting
the A5 register to reference your global data. The functionsA5Size
andA5Init
are supplied for creating an A5 world.
BWInitialize
calls a local function namedInitGlobalData
to initialize any global variables that are defined by the background picture printing extension. This function is shown in Listing 2-7.
Listing 2-7 The
- IMPORTANT
- You must perform the actual initialization of your global variables in
a function that you call from your override of theGXInitialize
message. This is because some compilers optimize code in a way that can invalidate the A5 register. This is why theBWInitialize
override function calls theInitGlobalData
function to initialize the globals.![]()
InitGlobalData
function
OSErr InitGlobalData() { gBackwashShape = nil; return noErr; }This function initializes thegBackwashShape
variable tonil
to indicate that it is currently unused.Adding a Background Picture Panel to the Print Dialog Box
The background picture printing extension adds a panel to the Print dialog box. The placeholders for this panel are defined in thebackwash.r
resource file; however, several of the values need to be filled in at run time. In the background picture printing extension, the override of theGXJobPrintDialog
message,BWJobPrintDialog
, performs this operation by calling a local function,SetupPrintPanel
, and then forwarding theGXJobPrintDialog
message to the rest of the message handlers. TheGXJobPrintDialog
message is described on page 4-84 in the chapter "Printing Messages." TheBWJobPrintDialog
function is shown in Listing 2-8.Listing 2-8 The
BWJobPrintDialog
override function
OSErr BWJobPrintDialog(gxDialogResult *dlogResult) { OSErr err; err = SetUpPrintPanel(); if (!err) err = Forward_GXJobPrintDialog(dlogResult); return err; }The local functionSetupPrintPanel
performs the work of setting up the panel
that the background picture extension adds to the Print dialog box. It uses the
Collection Manager to store and access aBackwashCollection
structure. TheBackwashCollection
structure is defined as shown in Listing 2-9. The Collection Manager is described in Inside Macintosh: QuickDraw GX Environment and Utilities.Listing 2-9 The
BackwashCollection
structure
struct BackwashCollection { long intensity; unsigned char addBackwash; Boolean haveFileInfo; FSSpec fileInfo; }; typedef struct BackwashCollection BackwashCollection;The fields of this structure are used as follows:
The
Field Description
intensity
- The intensity level (a percentage value) that the user has selected for drawing the background picture on the page.
addBackwash
- A value that indicates whether the user has enabled the background picture printing extension.
haveFileInfo
- A Boolean value that indicates whether the user has entered a picture filename into the panel.
fileInfo
- The file system specification of the picture file that the user chose to use as the background picture.
SetupPrintPanel
function first attempts to access the background picture collection item from the job collection, which is the collection of items maintained by
the Collection Manager for the print job that is printing. If this is the first time that the background picture extension has been accessed, the item is not found, andSetupPrintPanel
creates a new item with default values and adds it to the collection. Finally,SetupPrintPanel
sets up the panel information for the Dialog Manager and calls theGXSetupDialogPanel
function to actually add the panel to the dialog box. TheGXSetupDialogPanel
function is described on page 5-28 in the chapter "Printing Functions for Message Overrides." The code for theSetupPrintPanel
function is shown in Listing 2-10.Listing 2-10 The
SetupPrintPanel
function
OSErr SetUpPrintPanel() { OSErr noErr; gxPanelSetupRecord panelSetupRec; BackwashCollection backwashConfig; /* Get the job collection and then try to find the backwash collection item in there. */ jobCollection = GXGetJobCollection(GXGetJob()); /* call local function to get settings */ err = GetJobCollectionItem(&backwashConfig, nil, kBackwashCollectionType, kBackwashSettingsID); /* If the collection item doesn't yet exist, create a new item, set it up with default values, and add it to the job collection */ if (err == collectionItemNotFoundErr) { backwashConfig.addBackwash = kDontAddBackwash; backwashConfig.haveFileInfo = false; backwashConfig.intensity = kDefaultIntensity; strcpy(&backwashConfig.fileInfo.name[1], "none"); backwashConfig.fileInfo.name[0] = strlen(&backwashConfig.fileInfo.name[1]); /* call local function to store settings */ err = StoreJobCollectionItem(&backwashConfig, sizeof(BackwashCollection), kBackwashCollectionType, kBackwashSettingsID, true); } nrequire(err, GetSettings_Failed); /* Set up the panel: store the ID of the panel resource to use, the resource file in which it is located, and the type of panel that is being stored. */ panelSetupRec.panelResId= r_BackwashPanel; /* resource ID */ /* resource file */ panelSetupRec.resourceRefNum = GXGetMessageHandlerResFile(); panelSetupRec.refCon = 0; /* not used*/ panelSetupRec.panelKind = gxExtensionPanel; /* panel type */ err = GXSetupDialogPanel(&panelSetupRec); GetSettings_Failed: return err; }SetupPrintPanel
returns an error code of typeOSErr
, which is the same type of function result that is returned by all of the functions that it calls.SetupPrintPanel
also uses the error code to detect when it needs to initially add its own item to the
job collection.
- WARNING
- The size of the items in the job collection is subject to change as QuickDraw GX evolves. For that reason, it is important for you to specify an expected size for each item in your calls to the Collection Manager, rather than specifying
nil
, which tells the Collection Manager to copy the entire object no matter what its size is. The size that you specify must match the size of the data structure into which the item
is being copied.![]()
Handling an Event in the Background Picture Panel
When a user event occurs in the background picture panel, the background picture printing extension needs to examine the event and determine whether or not to handle it. QuickDraw GX sends theGXHandlePanelEvent
message whenever an event occurs in a printing panel. TheGXHandlePanelEvent
message is described on page 4-85 in the chapter "Printing Messages." Included with the message is a structure of typegxPanelInfoRecord
, which is declared as shown in Listing 2-11.Listing 2-11 The
gxPanelInfoRecord
structure
struct gxPanelInfoRecord { gxPanelEvent panelEvt; /* the event */ short panelResId; /* resource ID of current 'ppnl' res */ DialogPtr pDlg; /* pointer to dialog box */ EventRecord *theEvent; /* pointer to event */ short itemHit; /* actual item number of event */ short itemCount; /* number of items before your items */ short evtAction; /* the action that occurs after this event is processed */ short errorStringId; /* ID of error string */ gxFormat theFormat; /* the current format */ void *refCon; /* refCon from gxPanelSetupRecord */ }; typedef struct gxPanelInfoRecord gxPanelInfoRecord;The fields of this structure are described in the section "The Panel Information Structure" on page 4-35 in the chapter "Printing Messages."Panel events and the way that they are processed are described in Inside Macintosh: QuickDraw GX Printing.
In the background picture printing extension, the override of the
GXHandlePanelEvent
message,BWHandlePanelEvent
, responds to the panel events that are shown inTable 2-4. The list of possible panel events that you can respond to is given in the section "Panel Events" on page 4-36 in the chapter "Printing Messages."The
BWHandlePanelEvent
function, which is shown in Listing 2-12, operates as aswitch
statement that selects the events of interest (those listed in Table 2-4) and provides code to handle each. ThegxPanelOpenEvt
case is handled by calling a local function,OpenBackwashPanel
, which is shown in Listing 2-13 on page 2-24.Listing 2-12 The
BWHandlePanelEvent
override function
OSErr BWHandlePanelEvent(gxPanelInfoRecord *panelInfo) { OSErr err = noErr; GrafPtr oldPort; DialogPtr pDlg; StandardFileReply reply; SFTypeList typeList; BackwashCollection backwashConfig; Handle hItem; Rect itemRect; short itemType; pDlg = panelInfo->pDlg; GetPort(&oldPort); SetPort(pDlg); switch (panelInfo->panelEvt) /* select events of interest */ { /* if the panel is opening, initialize it */ case gxPanelOpenEvt: OpenBackwashPanel(pDlg, panelInfo->itemCount); break; /* If the user clicks the Select Picture button, prompt for the name of a file to load. If the user selects one, access the collection item, move the user's file specification to the collection, and replace the old collection item with the modified version. */ case gxPanelHitEvt: if (panelInfo->itemHit == (panelInfo->itemCount + d_SelectPicture)) { typeList[0] = 'PICT'; StandardGetFile(nil, 1, typeList, &reply); require(replay.sfGood, UserCancelledFileDialog); err = GetJobCollectionItem(&backwashConfig, nil, kBackwashCollectionType, kBackwashSettingsID); nrequire(err, GetSettings_Failed); backwashConfig.haveFileInfo = true; BlockMove(&reply.sfFile, &backwashConfig.fileInfo, sizeof(FSSpec)); /* replace old collection item with updated version */ err = StoreJobCollectionItem(&backwashConfig, sizeof(BackwashCollection), kBackwashCollectionType, kBackwashSettingsID, false); nrequire(err, StoreSettings_Failed); /* update the filename dialog item */ GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_FileNameItem, &itemType, &hItem, &itemRect); SetIText(hItem, &backwashConfig.fileInfo.name); } break; /* If the panel is activating or deactivating, or if the focus (the section of the dialog box that is active) is changed, either activate or deactivate the picture intensity field. */ case gxPanelActivateEvt: case gxPanelDeactivateEvt: case gxPanelIconFocusEvt: case gxPanelPanelFocusEvt: if ((((DialogPeek) pDlg)->editField +1) == (panelInfo->itemCount +d_intensity)) { if ((panelInfo->panelEvt == gxPanelPanelFocusEvt) TEActivate(((DialogPeek) pDlg)->textH); else TEDeactivate(((DialogPeek) pDlg)->textH); } break; } UserCancelledFileDialog: GetSettings_Failed: StoreSettings_Failed: SetPort(oldPort); return err; }TheOpenBackwashPanel
function in Listing 2-13 is a local function called by theBWHandlePanelEvent
function to initialize the background picture panel when the user opens it. This function performs any initialization of the panel that cannot be performed by'xdtl'
resource specifications, which are described in Inside Macintosh: QuickDraw GX Printing.Listing 2-13 The
OpenBackwashPanel
function
/* OpenBackwashPanel handles non-'xdtl' item initialization when the panel is opened. Note that the items are offset from the the value of itemCount, which means that item #5 in the panel is accessed by passing the value itemCount+5. */ void OpenBackwashPanel(DialogPtr pDlg, short itemCount) { BackwashCollection backwashConfig; Handle hItem; Rect itemRect; short itemType; /* Initialize the current filename displayed, based on the settings in the backwash collection item. */ GetJobCollectionItem(&backwashConfig, nil, kBackwashCollectionType, kBackwashSettingsID); GetDItem(pDlg, itemCount +d_FileNameItem, &itemType, &hItem, &itemRect); SetIText(hItem, &backwashConfig.fileInfo.name); }TheOpenBackwashPanel
function initializes the dialog box panel by filling in the filename that is stored in its configuration information. It operates by retrieving the configuration information from the job collection and storing the filename from the configuration into the filename item in the panel.Storing the Background Picture in the Spool File
The background picture printing extension draws the background picture on each page. To do this, it first stores the background picture as a resource in the spool file. It then accesses that resource and applies the picture to each page as the page is being despooled.To store the picture in the spool file, the background picture printing extension overrides the
GXCreateSpoolFile
message, which QuickDraw GX sends at the start of spooling a document. TheGXCreateSpoolFile
message is described on page 4-68 in the chapter "Printing Messages." In the background picture printing extension, the override ofGXCreateSpoolFile
,BWCreateSpoolFile
, is shown in Listing 2-14.Listing 2-14 The
BWCreateSpoolFile
override function
OSErr BWCreateSpoolFile(FSSpecPtr anFSSpec, long createOptions, gxSpoolFile *theSpoolFile) { gxGraphicsError grErr; BackwashCollection backwashConfig; /* forward the message so that the spool file is created */ grErr = (gxGraphicsError) Forward_GXCreateSpoolFile(anFSSpec, createOptions, theSpoolFile); nrequire(grErr, ForwardMessage_Failed); /* Get the collection item and see if extension is enabled and picture file info has been entered. If so, add the background picture to the spool file. */ grErr = (gxGraphicsError) GetJobCollectionItem(&backwashConfig, nil, kBackwashCollectionType, kBackwashSettingsID); if (!grErr && backwashConfig.addBackwash && backwashConfig.haveFileInfo) grErr = AddBackwash(&backwashConfig.fileInfo, (short) backwashConfig.intensity, *theSpoolFile); else if (grErr == collectionItemNotFoundErr) grErr = noErr; ForwardMessage_Failed: return (OSErr) grErr; }TheBWCreateSpoolFile
function first forwards theGXCreateSpoolFile
message to allow QuickDraw GX to use the default implementation (or other message handlers
to use their implementations) to create the spool file.BWCreateSpoolFile
then calls the local functionAddBackwash
to read the background picture file and store it in the spool file. TheAddBackwash
function is shown in Listing 2-15.Listing 2-15 The
AddBackwash
function
gxGraphicsError AddBackwash(FSSpecPtr fileInfo, short theIntensity, gxSpoolFile theSpoolFile) { gxGraphicsError grErr = noErr; Rect pictBounds; PicHandle backwashPict; /* use {1, 1} to mean don't stretch */ Point patStretchPoint = {1,1}; Handle gxShapeHdl = nil; gxShape gxPictShape; short resAttribs; /* load the picture and then convert it to a GX shape */ backwashPict = LoadAPict(fileInfo); require_action(backwashPict, CouldNotLoadPICT, grErr = nilHandleErr;); pictBounds = (*backwashPict)->picFrame; gxPictShape = GXNewShape(gxPictureType); /* picture shape */ nrequire_action(GXGetGraphicsError(&grErr), CouldNotCreateShape, KillPicture(backwashPict);); GXConvertPICTToShape(backwashPict, gxDefaultOptionsTranslation, &pictBounds, &pictBounds, patStretchPoint, gxPictShape, nil); KillPicture(backwashPict); /* get rid of original */ nrequire_action(GXGetGraphicsError(&grErr), CouldNotConvertShape, GXDisposeShape(gxPictShape);); /* lighten the picture shape as specified by user */ grErr = SetShapeIntensity(gxPictShape, theIntensity); nrequire_action(grErr, CouldNotSetShapeIntensity, GXDisposeShape(gxPictShape);); /* flatten picture shape into handle */ gxShapeHdl = ShapeToHandle(gxPictShape); grErr = (gxGraphicsError) MemError(); GXDisposeShape(gxPictShape); nrequire(grErr, CouldNotFlattenShape); /* Add the picture shape as a resource in the spool file. After adding it, mark the resource as "sysHeap, purgeable", so that it won't be loaded into PrinterShare's heap, which is small. */ grErr = Send_GXSpoolResource(theSpoolFile, gxShapeHdl, kBackwashCollectionType, r_BackwashPICTID); nrequire_action(grErr, CouldNotAddShape, DisposHandle(gxShapeHdl);); resAttribs = GetResAttrs(gxShapeHdl); SetResAttrs(gxShapeHdl, resAttribs | resSysHeap | resPurgeable); ChangedResource(gxShapeHdl); WriteResource(gxShapeHdl); /* release the resource-DO NOT call DisposHandle on it */ ReleaseResource(gxShapeHdl); CouldNotLoadPICT: CouldNotCreateShape: CouldNotConvertShape: CouldNotSetShapeIntensity: CouldNotFlattenShape: CouldNotAddShape: return grErr; }TheAddBackwash
function stores the background picture as a resource in the spool file. AddBackwash first calls a local function,LoadAPict
, to read a picture file and create aPicHandle
for it. TheLoadAPict
function, which opens the specified file, creates the handle, skips over the file header information, and reads the picture data into the handle.AddBackwash
then converts the picture into a QuickDraw GX shape by calling theGXConvertPICTToShape
function, which is described in Inside Macintosh: QuickDraw GX Objects.Once
AddBackwash
has a shape object that represents the background picture, it calls a local function,SetShapeIntensity
, to lighten the background picture to the intensity level selected by the user (in the background picture panel).SetShapeIntensity
modifies the transfer object for each shape in the background picture to use blending, which lightens the final picture. The intensity is adjusted to match the value selected by the user.Finally,
AddBackwash
flattens the picture shape into a handle by calling the local functionShapeToHandle
and adds the flattened shape to the spool file as a resource. The resource is added in the system heap and is marked as purgeable.ShapeToHandle
calls theGXFlattenShape
function, which is described in Inside Macintosh: QuickDraw GX Objects.Adding the Background Picture to a Page
The background picture printing extension draws the background picture on each page as the page is being despooled. It does this by overriding theGXDespoolPage
message with theBWDespoolPage
function, which is shown in Listing 2-16. This function first forwards theGXDespoolPage
message, which is described on page 4-75 in the chapter "Printing Messages," so that any other message handlers that are going to modify the page can do so first, which allows the background picture to be drawn using the final shape of the page.Listing 2-16 The
BWDespoolPage
function
OSErr BWDespoolPage(gxSpoolFile theSpoolFile, long thePageNum, gxFormat theFormat, gxShape *thePage, Boolean *formatChanged) { OSErr anOSErr; gxGraphicsError grErr; Handle vpListHdl, gxShapeHdl = nil; gxRectangle pBounds; Fixed cx, cy, px, py; long numViewPorts; BackwashCollection backwashConfig; /* First, forward the message to make sure that the final page shape is being despooled. */ anOSErr = Forward_GXDespoolPage (theSpoolFile, thePageNum, theFormat, thePage, formatChanged); nrequire((grErr = (gxGraphicsError) anOSErr), ForwardMessage_Failed); grErr = GetJobCollectionItem(&backwashConfig, nil, kBackwashCollectionType, kBackwashSettingsID); nrequire_action((!grErr && backwashConfig.addBackwash && backwashConfig.haveFileInfo), NotAddingBackwash, grErr = noErr); /* Load the flattened shape resource (the background picture) if necessary. This is only needed for the first page because the shape reference is stored in a global variable. */ if (gBackwashShape == nil) anOsErr = Send_GXDespoolResource(theSpoolFile, kBackwashCollectionType, r_BackwasPICTID, &gxShapeHdl); nrequire((grErr = (gxGraphicsError) anOSErr), DespoolResource_Failed); /* If necessary, unflatten the background picture shape. Since a view port list is needed for the unflattening, use the current page's view port list. */ if (gBackwashShape == nil) /* first page */ { numViewPorts = GXGetShapeViewPorts(*thePage, nil); vpListHdl = TempNewHandle(numViewPorts * sizeof(gxViewPort), &anOSErr); grErr = (gxGraphicsError) anOSErr; nrequire_action(grErr, TempNewHandle_Failed, ReleaseResource(gxShapeHdl);); HLock(vpListHdl); GXGetShapeViewPorts(*thePage, (gxViewPort *) *vpListHdl); gBackwashShape = HandleToShape(gxShapeHdl, numViewPorts, (gxViewPort *) *vpListHdl); DisposHandle(vpListHdl);/* all done with this */ if (gxShapeHdl) ReleaseResource(gxShapeHdl); nrequire(GXGetGraphicsError(&grErr), CouldNotSetUpShape); } /* Now use the current page format to obtain the page dimensions. Use the dimensions and the bounds of the picture shape to center the page. Once the shape is centered, add it behind the shapes that constitute the contents of the page. */ GXGetFormatDimensions(theFormat, &pBounds, nil); grErr = GXGetJobError(GXGetJob()); nrequire(grErr, GetFormatDims_Failed); cx = pBounds.left + (pBounds.right - pBounds.left) >>1; cy = pBounds.top + (pBounds.bottom - pBounds.top) >>1; GXGetShapeBounds(gBackwashShape, 0, &pBounds); px = pBounds.left + (pBounds.right - pBounds.left) >>1; py = pBounds.top + (pBounds.bottom - pBounds.top) >>1; GXMoveShapeTo(gBackwashShape, cx-px, cy-py); GXSetPictureParts(*thePage, 1, 0, 1, &gBackwashShape, nil, nil, nil); GXGetGraphicsError(&grErr); ForwardMessage_Failed: NotAddingBackwash: DespoolResource_Failed: TempNewHandle_Failed: CouldNotSetUpShape: GetFormatDims_Failed: return grErr; }After forwarding theGXDespoolPage
message,BWDespoolPage
makes sure that the background-picture-configuration collection item is available in the job collection. If the item is not found in the job collection, it means that the background picture panel was never opened because that event triggers the adding of the item to the collection. If the panel was never opened, then the user is printing without dialog boxes, and no background picture is added to the pages. In this case,BWDespoolPage
does not return an error because the function did not really fail. This is accomplished in thenrequire_action
call, which changes the error code tonoErr
and branches to the end of the function.If the background-picture-configuration collection item is found,
BWDespoolPage
checks to see if the global variable for the background picture shape isnil
. If so,BWDespoolPage
reads the resource from the spool file and unflattens it into a
picture shape.Finally,
BWDespoolPage
centers the background picture shape on the page and adds the shape to the page contents.Closing the Spool File
When QuickDraw GX finishes with the spooling of a document, it sends theGXCloseSpoolFile
message, which is described on page 4-79 in the chapter "Printing Messages." The background picture printing extension overrides this message with theBWCloseSpoolFile
function, shown in Listing 2-17, to dispose of the picture shape that it allocated for the background picture.Listing 2-17 The
BWCloseSpoolFile
override function
OSErr BWCloseSpoolFile(gxSpoolFile theSpoolFile, long closeOptions) { if (gBackwashShape != nil) { GXDisposeShape(gBackwashShape); gBackwashShape = nil; } return Forward_GXCloseSpoolFile(theSpoolFile, closeOptions); }Shutting Down the Background Picture Extension Environment
When the background picture printing extension is finished, it needs to deallocate the globals that it allocated in its override of theGXInitialize
message. In the background picture extension, the override of theGXShutDown
message,BWShutDown
, calls theDisposeMessageGlobals
function to do that deallocation, as shown in Listing 2-18. TheGXShutDown
message is described on page 4-44 in the chapter "Printing Messages."Listing 2-18 The
BWShutDown
override function
OSErr BWShutDown() { DisposeMessageGlobals(); return noErr; }
Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help